"""Log into gitlab.com via SSO."""
import argparse
import base64
import os
import pathlib
import subprocess
import tempfile
import typing
from urllib import parse
import xml.etree.ElementTree as ET

from cki_lib import misc
from cki_lib import retrying
from cki_lib.session import get_session
import gitlab
import requests_gssapi
import sentry_sdk


def _find_assertion(node: ET.Element) -> typing.Optional[str]:
    for child in node:
        if child.tag.lower().endswith('input'):
            if child.attrib['name'] == 'SAMLResponse':
                return child.attrib['value']
        elif (ret := _find_assertion(child)) is not None:
            return ret
    return None


@retrying.retrying_on_exception(subprocess.CalledProcessError)
def _kinit(
    principal: str,
    keytab: typing.Optional[bytes] = None,
    password: typing.Optional[str] = None,
) -> None:
    if keytab:
        with tempfile.TemporaryDirectory() as tempdirname:
            keytab_path = pathlib.Path(tempdirname, 'keytab')
            keytab_path.write_bytes(keytab)
            subprocess.run(['kinit', '-kt', keytab_path, principal], check=True)
    else:
        subprocess.run(['kinit', principal], input=password, encoding='utf8', check=True)


def main(args: list[str] | None = None) -> None:
    # pylint: disable=too-many-locals
    """Log into gitlab.com via SSO."""
    parser = argparse.ArgumentParser()
    parser.parse_args(args)

    session = get_session('cki.cki_tools.gitlab_sso_login', raise_for_status=True)

    keytab = os.environ.get('KRB_KEYTAB')
    principal = os.environ.get('KRB_PRINCIPAL')
    password = os.environ.get('KRB_PASSWORD')
    sso_saml_auth_url = os.environ['SSO_SAML_AUTH_URL']
    gitlab_saml_cb_url = os.environ['GITLAB_SAML_CB_URL']
    smoke_test_project_url = os.environ.get('SMOKE_TEST_PROJECT_URL')

    with tempfile.TemporaryDirectory() as tempdirname:
        if principal:
            os.environ['KRB5CCNAME'] = pathlib.Path(tempdirname, 'cache').as_posix()
            _kinit(principal,
                   keytab=base64.b64decode(os.environ.get(keytab, '')) if keytab else None,
                   password=os.environ.get(password, '') if password else None)

        saml_auth = requests_gssapi.HTTPSPNEGOAuth(mutual_authentication=requests_gssapi.OPTIONAL)
        response = session.get(sso_saml_auth_url, auth=saml_auth, allow_redirects=False)
        if not (assertion := _find_assertion(ET.fromstring(response.text))):
            raise Exception('Unable to find SAML assertion')
        session.post(gitlab_saml_cb_url, data={"SAMLResponse": assertion})

    if smoke_test_project_url:
        url_parts = parse.urlsplit(smoke_test_project_url)
        instance_url = parse.urlunparse(url_parts[:2] + ('',) * 4)
        gl_project = gitlab.Gitlab(instance_url, session=session).projects.get(url_parts.path[1:])
        print(f'Project found: {gl_project.web_url}')


if __name__ == '__main__':
    misc.sentry_init(sentry_sdk)
    main()
